using System;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SnmpSharpNet;
using System.IO;
using System.Windows.Forms;

namespace Thompson
{
    
    public class Tx
    {
        public Mutex _txMutex = new Mutex();

        // Information which is referenced by the main application and
        // which is protected by the _txMutex.
        public string _guiText = "Waiting to Initialize SNMP";
        public bool   _plcHeating = false;
        public bool   _plcAlarm = false;
        public byte   _command = 0;
        public byte   _channel = 17;
        public byte   _powerLevel = 4;
        public byte   _modulation = 4;

        /*****************************
         * Declare global enumerations
         *****************************/
        public enum E_Operator
        {
            LocalMode,
            RemoteWWW,
            Remote,
            Maintenance,
            Unknown
        }
        public enum E_SwitchOverMode
        {
            Unknown,
            Manual,
            Auto
        }
        public enum E_CommandTx
        {
            Unknown=0,
            GeneralShutdown,
            CoolingOn,
            HeatingOn,
            CircuitBreakerOn,
            GeneralOn 
            
        }
        public enum E_CommandModulationMode
        {
            DRM=0,  
            AM=16,
            DCC1=17,
            DCC2=18,
            DCC3=19,
            DCC4=20,
            AMC=21,
            EHC=22,
            SSB=31,
            SSB1=32,
            SSB2=42,
            SSB3=43,
            simulcast=96,
            simulcast_right=97,
            symetrical_simulcast=98,
            scs_right=112,
            scs_left=113,
            Unknown
        }
        public enum E_CommandPower
        {
            Unknown,
            PlusOne,        // plus 1%
            LessOne,        // less 1%
            NormalPower,    // normal (93% for sw250kw)
            ReducedPower,   // reuced (47% for sw250kw)
            OperatorPower,  // selectable value for sw250kw
            Maximum         // specific predefined value for sw500kw only
        }
        public enum E_TxLinkStatus
        {
            Initializing=0,
            Connected,
            ChangingValues,
            ChangingValuesRetry,
            NoResponse,
            ConnectionFailed
        }

        /*******************************
         * Declare global properties
         *******************************/

        //Create instance of log data for logging from this file
        AppMain.LogData log = AppMain.LogData.GetObject;

        //Control properties from Function group
        public string IPaddress { get; set; }
        private int _scanTime;
        private const int RetryTimeout = 5000;
        private bool _commEnabled = false;
        private bool _connecting = false;
        private bool _commWasEnabled = false;
        private bool _snmpCommPortOpen = false;
        private bool _configuringSnmpComms = false;
        private SimpleSnmp _snmpConnection;
        public string GuiText { get; set; }
        public int txNum { get; set; }
        public bool writeEnabled { get; set; }
        
        TxItem TxNameObj = new TxItem { Description = "Transmitter name: ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.1.2.1.0", CurrentValue = "???", ReadOnly = true };
        TxItem SwitchOverModeObj = new TxItem { Description = "Adaptation: ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.1.2.11.0", CurrentValue = E_SwitchOverMode.Unknown, ReadOnly = true, RequestValue = E_SwitchOverMode.Auto };
        TxItem OperatorObj = new TxItem { Description = "Operation Mode: ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.1.2.12.0", CurrentValue = E_Operator.Unknown, ReadOnly = true, RequestValue = E_Operator.Remote };
        TxItem FrequencyNumberObj = new TxItem { Description = "Channel Number: ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.1.2.5.0", CurrentValue = (int)0, ReadOnly = true };
        TxItem RequestedPowerPercentObj = new TxItem { Description = "Requested Power (%): ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.1.2.9.0", CurrentValue = (int)0, ReadOnly = true };
        
        //Read-only properties from Measurement group
        public TxItem MeasAudioInputLevelObj = new TxItem { Description = "Audio Input Level (%): ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.4.1.1.4.1.0", CurrentValue = (int)0, ReadOnly = true };
        public TxItem MeasRfGridCurrentObj = new TxItem { Description = "RF Grid Current (x0.01 A): ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.4.1.1.4.1.1", CurrentValue = (int)0, ReadOnly = true };
        public TxItem MeasRfGridVoltageObj = new TxItem { Description = "RF Grid Voltage (V): ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.4.1.1.4.1.2", CurrentValue = (int)0, ReadOnly = true };
        public TxItem MeasRfPowerObj = new TxItem { Description = "RF Power (kW): ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.4.1.1.4.1.3", CurrentValue = (int)0, ReadOnly = true };
        public TxItem MeasRfDifferentialTemperatureObj = new TxItem { Description = "RF Diff. Temp. (X 0.1 degrees): ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.4.1.1.4.1.4", CurrentValue = (int)0, ReadOnly = true };
        public TxItem MeasRfAnodeCurrentObj = new TxItem { Description = "RF Anode Current (x0.1 A): ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.4.1.1.4.1.5", CurrentValue = (int)0, ReadOnly = true };
        public TxItem MeasRfScreenCurrentObj = new TxItem { Description = "RF Screen Current (x0.01 A): ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.4.1.1.4.1.6", CurrentValue = (int)0, ReadOnly = true };
        public TxItem MeasRfAnodeVoltageObj = new TxItem { Description = "RF Anode Voltage (x0.1 kV): ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.4.1.1.4.1.7", CurrentValue = (int)0, ReadOnly = true };
        public TxItem MeasRfMainsVoltageObj = new TxItem { Description = "RF Mains Voltage (V): ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.4.1.1.4.1.8", CurrentValue = (int)0, ReadOnly = true };
        public TxItem MeasVSWRObj = new TxItem { Description = "VSWR (x0.01): ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.4.1.1.4.1.9", CurrentValue = (int)0, ReadOnly = true };
        public TxItem MeasReflectedPowerObj = new TxItem { Description = "Reflected Power (kW): ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.4.1.1.4.1.10", CurrentValue = (int)0, ReadOnly = true };
        
        //Command group properties
        TxItem CmdTxObj = new TxItem { Description = "Command TX Mode: ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.5.1.1.0", CurrentValue = E_CommandTx.Unknown };
        TxItem CmdModulationObj = new TxItem { Description = "Modulation Type: ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.5.1.101.1.0", CurrentValue = E_CommandModulationMode.Unknown };
        TxItem CmdPowerObj = new TxItem { Description = "Command Power Mode: ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.5.1.3.0", CurrentValue = E_CommandPower.Unknown, RequestValue = E_CommandPower.OperatorPower };
        TxItem PowerLevelObj = new TxItem { Description = "Command Power Level (%): ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.5.1.4.0", CurrentValue = (int)0 };
        TxItem CmdFreqNumObj = new TxItem { Description = "Command Channel Number: ", Oid = ".1.3.6.1.4.1.8711.1.1.10.14.1.5.1.12.0", CurrentValue = (int)0 };
        
        //Create new dictionary for collecting SNMP data
        Dictionary<string, TxItem> TxData = new Dictionary<string, TxItem>();

        //Create timer for polling data from Transmitter
        public System.Threading.Timer tmrScanTx;

        //Declaration for Application to respond to Transmitter data received
        public delegate void TxResponse(TxResult responseData);
        //public event TxResponse OnTxResponse;

        public Tx(int newTxNum)
        {
            this.txNum = newTxNum;
            //Add each TxItem to the dictionary
            TxData.Add(CmdTxObj.Oid, CmdTxObj);
            TxData.Add(FrequencyNumberObj.Oid, FrequencyNumberObj);
            TxData.Add(CmdFreqNumObj.Oid, CmdFreqNumObj); //
            TxData.Add(RequestedPowerPercentObj.Oid, RequestedPowerPercentObj);
            TxData.Add(CmdPowerObj.Oid, CmdPowerObj);  //
            TxData.Add(PowerLevelObj.Oid, PowerLevelObj);
            TxData.Add(CmdModulationObj.Oid, CmdModulationObj); //
            TxData.Add(OperatorObj.Oid, OperatorObj);//
            TxData.Add(SwitchOverModeObj.Oid, SwitchOverModeObj);//
            TxData.Add(MeasAudioInputLevelObj.Oid, MeasAudioInputLevelObj);
            TxData.Add(MeasReflectedPowerObj.Oid, MeasReflectedPowerObj);
            TxData.Add(MeasRfAnodeCurrentObj.Oid, MeasRfAnodeCurrentObj);
            TxData.Add(MeasRfAnodeVoltageObj.Oid, MeasRfAnodeVoltageObj);
            TxData.Add(MeasRfDifferentialTemperatureObj.Oid, MeasRfDifferentialTemperatureObj);
            TxData.Add(MeasRfGridCurrentObj.Oid, MeasRfGridCurrentObj);
            TxData.Add(MeasRfGridVoltageObj.Oid, MeasRfGridVoltageObj);
            TxData.Add(MeasRfMainsVoltageObj.Oid, MeasRfMainsVoltageObj);
            TxData.Add(MeasRfPowerObj.Oid, MeasRfPowerObj);
            TxData.Add(MeasRfScreenCurrentObj.Oid, MeasRfScreenCurrentObj);
            TxData.Add(MeasVSWRObj.Oid, MeasVSWRObj);
            TxData.Add(TxNameObj.Oid, TxNameObj);
        }

        //Method used to initialize parameters for communicating with the transmitter and enable polling
        public void StartCommunication(string setIpAddress, int scanTime)
        {
            //Set PLC communication parameters
            this.IPaddress = setIpAddress;
            // Run the timer in a separate thread, this handles multithreading
            // completely, no other operations are required to make this
            // run in a separate thread.
            // Mutexes have been implemented to allow the system to work properly
            // (avoid reading and writing the same variables from different threads
            // at the same time).
            tmrScanTx = new System.Threading.Timer(tmrScanTx_Tick, null, scanTime, scanTime);
            _scanTime = scanTime;
            //Enable cyclic read-write to Transmitter
            _commEnabled = true;
            GuiText = "Initializing Communications...";
        }

        void tmrScanTx_Tick(object sender)
        {
            //If there was an error, decrease the update rate
            if (_commEnabled == false)
            {
                tmrScanTx.Change(RetryTimeout, RetryTimeout);
            }
            else
            {
                if (!_commWasEnabled)
                {
                    tmrScanTx.Change(_scanTime, _scanTime);
                }
            }
            _commWasEnabled = _commEnabled;
            //Open SNMP if not already connected
            if (_snmpCommPortOpen == false)
            {
                // Note: This timer runs in a separate thread. There is no need for any
                // unique operations here.
                if (!_connecting)
                {
                    connect();
                }
            }
            else
            {
                // if comms drop out, catch it before major problems occur.
                if (!_snmpConnection.Valid)
                {
                    _channel = 17;
                    _powerLevel = 4;
                    _modulation = 4;
                    _snmpCommPortOpen = false;
                    return;
                }
                //Don't start if we're still waiting for a response
                if (_configuringSnmpComms == false)
                {
                    //run method ReadWrite() in a background thread
                    //System.Threading.Thread ReadWriteThread = new System.Threading.Thread(ReadWrite);
                    //ReadWriteThread.Start();
                    ReadWrite();
                }
            }
        }
         
        /*************************************************
         * Begin SetValue methods
         *************************************************/
        private void SetFrequencyNumber (int newFrequencyNumber)
        {
            //Update the value
            int maxFreqNum = 16;
            int minFreqNum = 1;
            int setFreqNum;
            //Bound the new set Frequency
            if (newFrequencyNumber > maxFreqNum) setFreqNum = maxFreqNum;
            else if (newFrequencyNumber < minFreqNum) setFreqNum = minFreqNum;
            else setFreqNum = newFrequencyNumber;
            //Write the new Request value
            FrequencyNumberObj.RequestValue = setFreqNum;   // This item is read-only, but helps us alarm
            CmdFreqNumObj.RequestValue = setFreqNum;            // This item is the one the TX should respond to

            //Because the TX ignores the new Freq command when in GeneralOn, set the CmdTx to CircuitBreakerOn
            
        }
        public void OnTxResponse(Thompson.TxResult responseData)
        {
            // keep other threads from accessing this information until it has been writted
            _txMutex.WaitOne();
            //Update Local Information which is updated in UI
            _guiText = responseData.guiText;
            //
            //G_TX[responseData.txNum].WriteSnmpStatus(responseData.guiText);

            if (responseData.result != null)
            {
                //Convert the result into the TxItems
                ConvertSnmpData(responseData.result);

                //Update the Gui with the current text using actual SNMP data
                _guiText = ReadStatusString();
                //G_TX[responseData.txNum]WriteSnmpStatus(C_TX[responseData.txNum].ReadStatusString());

                //Update the PLC Heating info
                _plcHeating = GetHeating();

                //Update the display status from the Transmitter
                foreach (string key in TxData.Keys)
                {
                    switch(TxData[key].Description)
                    {
                        case "Channel Number: ":
                        {
                            _channel = (byte)TxData[key].CurrentValue;
                            break;
                        }
                        case "Requested Power (%): ":
                        {
                            _powerLevel = (byte)TxData[key].CurrentValue;
                            break;
                        }
                        case "Modulation Type: ":
                        {
                            _modulation = (byte)TxData[key].CurrentValue;
                            break;
                        }
                        case "Command TX Mode: ":
                        {
                            _command = (byte)TxData[key].CurrentValue;
                            break;
                        }
                    }
                }
            }
            _txMutex.ReleaseMutex();
        }
        private void SetCmdTx(E_CommandTx newCmdTx)
        {
            //Update the value
            CmdTxObj.RequestValue = newCmdTx;
        }
        private void SetCmdModulation (E_CommandModulationMode newCmdModulation)
        {
            //Update the values
            CmdModulationObj.RequestValue = newCmdModulation;
        }
        private void SetPowerLevel(int newPowerLevel)
        {
            //Update the value
            int maxPower = 99;
            int minPower = 20;
            int setPower;
            
            // TX can't respond to values above 35% when not in GeneralOn.
            if (CmdTxObj.CurrentValue == E_CommandTx.GeneralOn)
                maxPower = 99;
            else
                maxPower = 35;
            
            //Bound power to min/max values
            if (newPowerLevel > maxPower)
                setPower = maxPower;
            else if (newPowerLevel < minPower)
                setPower = minPower;
            else
                setPower = newPowerLevel;

            //Put the new value in the Request
            PowerLevelObj.RequestValue = setPower;
            RequestedPowerPercentObj.RequestValue = setPower;

            if (RequestedPowerPercentObj.FaultCount > 4)
            {
                if (CmdPowerObj.CurrentValue == E_CommandPower.OperatorPower)
                {
                    CmdPowerObj.RequestValue = E_CommandPower.LessOne;
                }
                else
                {
                    CmdPowerObj.RequestValue = E_CommandPower.OperatorPower;
                    RequestedPowerPercentObj.FaultCount = 1;
                }
            }
        }

        public bool GetHeating()
        {
            //Public function returns true if Tx is on and heating
            if (
                (this.CmdTxObj.CurrentValue == E_CommandTx.HeatingOn) ||
                (this.CmdTxObj.CurrentValue == E_CommandTx.GeneralOn) ||
                (this.CmdTxObj.CurrentValue == E_CommandTx.CircuitBreakerOn))
                return true;
            else
                return false;
        }

        public bool GetAlarm()
        {
            return _plcAlarm;
        }

        private void CheckAlarm()
        {
            /*****************************************************
             * Checks to see if a request has been ignored and 
             * returns true if there is an alarm status
             *****************************************************/
            int maxFaultCount = 30;
            int alarmFaultCount = 10;
            bool alarm = false;

            //If writing not enabled, lower threshold for earlier display of pending data
            
            //Check each item in the dictionary
            foreach (var txitem in TxData)
            {
                if (txitem.Value.RequestValue != null)
                {
                    //Item has a Request value. Increment fault counter if Request Value != Current Value
                    if (txitem.Value.RequestValue != txitem.Value.CurrentValue)
                    {
                        //Increment fault counter
                        txitem.Value.FaultCount++;

                        if (txitem.Value.FaultCount > maxFaultCount)
                        {
                            //Upper bound to fixed value to prevent overflow
                            txitem.Value.FaultCount = maxFaultCount;
                        }
                        else if(writeEnabled == true)
                        {
                            if (txitem.Value.ReadOnly == true)
                            {
                                //Only write log entries up to maxFaultCount, then stop to prevent flooding
                                log.Write(string.Format("{0} Current: {1} Requested: {2} Try Count: {3}",
                                                txitem.Value.Description,
                                                txitem.Value.CurrentValue,
                                                txitem.Value.RequestValue,
                                                txitem.Value.FaultCount));
                            }
                            else
                            {
                                //Only write log entries up to maxFaultCount, then stop to prevent flooding
                                log.Write(string.Format("{0} Current: {1} Requested: {2} (Read Only)",
                                                txitem.Value.Description,
                                                txitem.Value.CurrentValue,
                                                txitem.Value.RequestValue));
                            }
                        }
                        //If Fault count is above threshold, set alarm true
                        if (txitem.Value.FaultCount >= alarmFaultCount)
                        {
                            alarm = true;
                        }

                        //If writing is not enabled, just display all differences in request and current
                        if (writeEnabled == false)
                        {
                            alarm = true;
                            //Fault count is 0 when writing is not enabled
                            txitem.Value.FaultCount = 1;
                        }
                    }
                    else
                    {
                        //Items match, there is no fault
                        txitem.Value.FaultCount = 0;
                    }
                    
                }
            }

            //Return the alarm status
            _plcAlarm = alarm;
        }
		 
        private void connect()
        {
            //Create SNMP connection to the IP address of this Transmitter
            _configuringSnmpComms = true;
            _connecting = true;
            try
            {
                _snmpConnection = new SimpleSnmp(this.IPaddress, "public");
            }
            catch
            {
                _guiText = "Error connecting to IP " + IPaddress;
                _snmpCommPortOpen = false;
                _connecting = false;
                _configuringSnmpComms = false;
                return;
            }
            if (!_snmpConnection.Valid)
            {
                // Write to log only if we have not already done so. We don't want to fill the log.) JH
                if (_commEnabled == true)
                {
                    log.Write(string.Format("SNMP agent host name/ip address ({0}) is invalid.", this.IPaddress));
                    //Disable further communication
                    OnTxResponse(new TxResult { txNum = this.txNum, guiText = "IP Found, No valid Replies! TX" + this.txNum.ToString(), linkStat = E_TxLinkStatus.NoResponse, result = null });
                    _commEnabled = false;
                }
                _snmpCommPortOpen = false;
                _connecting = false;
                return;
            }
            else
            {
                _snmpCommPortOpen = true;
                _connecting = false;
#warning "It is important to include a check for each OID here. Only the ones which work should be added to the collection"
                // Eventually, it will be very useful to check for local/remote status
                // at the transmitter and dynamically enable/disable OIDs accordingly.
                _guiText = "";
            }
            _configuringSnmpComms = false;
        }

        private void ReadWrite()
        {
            GetSnmpStatus();
            // This allows an Entry in the Log File to Indicate what was written
            CheckAlarm();
            //After data has been updated, set the new requested values in the transmitter
            if (this.writeEnabled)
                setSnmpIntegers();
        }

        private void GetSnmpStatus()
        {
            //Create new string array of the OID's to get
            string[] getOids = TxData.Keys.ToArray();

            //Execute SNMP Get and store results in new dictionary
            Dictionary<Oid, AsnType> result = null;
            try
            {
                result = _snmpConnection.Get(SnmpVersion.Ver1, getOids);
            }
            catch
            {
                log.Write("Unidentified Error Reading From Tranmitter at " + IPaddress);
            }
            if (result == null)
            {
                //Write log entry and show message box if data not received from SNMP Get. Only write once until we have communication again.
                if (_commEnabled)
                {
                    string msgtext = string.Format("No Get results received from Transmitter IP Address: {0}", this.IPaddress);
                    log.Write(msgtext);
                    OnTxResponse(new TxResult { txNum = this.txNum, guiText = "No Results! TX" + this.txNum.ToString(), linkStat = E_TxLinkStatus.NoResponse, result = null });
                    //Disable further communication
                    _commEnabled = false;
                }
                return;
            }
            else
            {
                _commEnabled = true;
            }
            
            //Instead of converting the SNMP GetResult (string) to the expected datatype in this thread, call the event in StartWindow so that it can manage the data in a thread-safe way
            //(assume this method is running on a different thread than the main application)
            OnTxResponse(new TxResult { txNum = this.txNum, guiText = "New results received from "+this.txNum.ToString(), linkStat = E_TxLinkStatus.Connected, result = result });

            //We assume the StartWindow will run this ConvertSnmpData method using the result via the OnTxResponse event
            //ConvertSnmpData(result);  //We won't run this here because we assume we're on a different thread
        }

        private void setSnmpIntegers()
        {
            // new Object list of items to be written
            List<Vb> setVbItems = new List<Vb>();

            //Add TxItems which need to be Set
            foreach (var txitem in TxData)
            {
                if (
                    (txitem.Value.RequestValue != null) //Item has a RequestValue
                    && (txitem.Value.RequestValue != txitem.Value.CurrentValue) //Current value does not match request
                    && (txitem.Value.ReadOnly == false) //Item is allowed to be written
                    )
                {
                    //Add element to Snmp Set List
                    setVbItems.Add(new Vb(new Oid(txitem.Value.Oid), new Integer32(Convert.ToInt32(txitem.Value.RequestValue))));
                    log.Write(string.Format("{0}Current: {1} Requested: {2}", txitem.Value.Description, txitem.Value.CurrentValue.ToString(), txitem.Value.RequestValue.ToString()));
                }
            }

            //Execute SNMP Get and store results in new dictionary
            if (setVbItems.Count > 0)
            {
                log.Write(string.Format("{0} Going to Write SNMP data now", TxNameObj.CurrentValue));

                Dictionary<Oid, AsnType> result = null;
                try
                {
                    result = _snmpConnection.Set(SnmpVersion.Ver2, setVbItems.ToArray());
                }
                catch
                {
                    log.Write("Unidentified Error Writing To Tranmitter at " + IPaddress);
                }
                if (result == null)
                {
                    //Write log entry and show message box if data not received from SNMP Get
                    string msgtext = ("No Set results received from Transmitter. IP Address: " + this.IPaddress);
                    log.Write(msgtext);
                    //Disable further communication
                    _commEnabled = false;
                    OnTxResponse(new TxResult { txNum = this.txNum, guiText = "Connection Invalid! TX" + this.txNum.ToString(), linkStat = E_TxLinkStatus.ConnectionFailed, result = null });
                    return;
                }
            }

        }

        public void SetNewCommands(RequestToTx newReq, SettingsTx setting)
        {
            //update the txItems with the new request from PLC
            if (newReq.Channel > 0)
            {
                //Modulation
                if (newReq.GenModulation == true)
                    this.SetCmdModulation(setting.ModulationGenerator[newReq.ModulationRequest]);
                else
                    this.SetCmdModulation(setting.ModulationNormal[newReq.ModulationRequest]);
                //Power
                if (newReq.GenPower == true)
                    this.SetPowerLevel(setting.PowerLevelGenerator[newReq.PowerRequest]);
                else
                    this.SetPowerLevel(setting.PowerLevelNormal[newReq.PowerRequest]);
                //Command
                this.SetCmdTx(newReq.Command);
                //Channel
                this.SetFrequencyNumber(newReq.Channel);
            }
            else
            {
                string error = (string.Format("Error in requested channel from PLC ({0})!", newReq.Channel));
                log.Write(error);
                GuiText = error;
            }
        }

        public string ReadStatusString()
        {
            /********************************
             * returns a simple string that contains all data to be displayed on a single label in the GUI
             * ******************************/
            StringWriter status = new StringWriter();
            
            if (GetAlarm() == true)
            {
                if (writeEnabled == true)
                {
                    //If there are any ignored requests, print them to the GUI here
                    status.WriteLine("\nError while sending requests! Check Log File");
                }
                else
                {
                    status.WriteLine("\nPending Requests from PLC:");
                }

                //Add Pending data
                foreach (var txitem in TxData)
                {
                    if (txitem.Value.FaultCount > 0)
                    {
                        if (    (writeEnabled == true) 
                            &&  (txitem.Value.ReadOnly == false))
                        {
                            status.WriteLine("{0} Current: {1} Requested: {2} Fault Count: {3}",
                                                    txitem.Value.Description,
                                                    txitem.Value.CurrentValue,
                                                    txitem.Value.RequestValue,
                                                    txitem.Value.FaultCount);
                        }
                        else
                        {
                            status.WriteLine("{0} Current: {1} Requested: {2} (Read Only)",
                                                 txitem.Value.Description,
                                                 txitem.Value.CurrentValue,
                                                 txitem.Value.RequestValue);
                        }
                    }
                }
            }
            return status.ToString();
        } //end public string ReadStatusString(bool writeEnabled)

        public void ConvertSnmpData(Dictionary<Oid, AsnType> result)        
        {
            /**************************************
             * Converts the SNMP result data to the expected data type defined in the declaration of each TxItem
             * Performs operation on the global TxItem dictionary
             * ************************************/
            //Process the SNMP result and copy to the TxObject dictionary
            foreach (KeyValuePair<Oid, AsnType> kvp in result)
            {
                TxData["." + kvp.Key.ToString()].DataType = SnmpConstants.GetTypeName(kvp.Value.Type);
                TxData["." + kvp.Key.ToString()].GetResult = kvp.Value.ToString();
            }

            //declare vars of each type to get correct conversion
            int testint = 0;
            string teststring = "0";
            E_CommandModulationMode testEnCommandModulationMode = E_CommandModulationMode.Unknown;
            E_CommandPower testEnCommandPower = E_CommandPower.Unknown;
            E_CommandTx testEnCommandTx = E_CommandTx.Unknown;
            E_Operator testEnOperator = E_Operator.Unknown;
            E_SwitchOverMode testEnSwitchOverMode = E_SwitchOverMode.Unknown;

            foreach (var txitem in TxData)
            {
                //log.WriteLine("Attempting to convert to type: {0} for item: {1}", txitem.Value.CurrentValue.GetType(), txitem.Value.Description);

                if (txitem.Value.CurrentValue.GetType() == teststring.GetType())
                {
                    //Expected type is string - copy directly
                    txitem.Value.CurrentValue = txitem.Value.GetResult;
                }
                else if (txitem.Value.CurrentValue.GetType() == testint.GetType())
                {
                    //Expected type is int - try to convert
                    txitem.Value.CurrentValue = Convert.ToInt32(txitem.Value.GetResult);
                }
                else if (txitem.Value.CurrentValue.GetType() == testEnCommandModulationMode.GetType())
                {
                    //Expected type is enum - try to convert enum, then put enum text in GetResult
                    int enumvalue = Convert.ToInt32(txitem.Value.GetResult);
                    txitem.Value.CurrentValue = (E_CommandModulationMode)enumvalue;
                    txitem.Value.GetResult = ((E_CommandModulationMode)enumvalue).ToString();
                }
                else if (txitem.Value.CurrentValue.GetType() == testEnCommandPower.GetType())
                {
                    //Expected type is enum - try to convert enum, then put enum text in GetResult
                    int enumvalue = Convert.ToInt32(txitem.Value.GetResult);
                    txitem.Value.CurrentValue = (E_CommandPower)enumvalue;
                    txitem.Value.GetResult = ((E_CommandPower)enumvalue).ToString();
                }
                else if (txitem.Value.CurrentValue.GetType() == testEnCommandTx.GetType())
                {
                    //Expected type is enum - try to convert enum, then put enum text in GetResult
                    int enumvalue = Convert.ToInt32(txitem.Value.GetResult);
                    txitem.Value.CurrentValue = (E_CommandTx)enumvalue;
                    txitem.Value.GetResult = ((E_CommandTx)enumvalue).ToString();
                }
                else if (txitem.Value.CurrentValue.GetType() == testEnOperator.GetType())
                {
                    //Expected type is enum - try to convert enum, then put enum text in GetResult
                    int enumvalue = Convert.ToInt32(txitem.Value.GetResult);
                    txitem.Value.CurrentValue = (E_Operator)enumvalue;
                    txitem.Value.GetResult = ((E_Operator)enumvalue).ToString();
                }
                else if (txitem.Value.CurrentValue.GetType() == testEnSwitchOverMode.GetType())
                {
                    //Expected type is enum - try to convert enum, then put enum text in GetResult
                    int enumvalue = Convert.ToInt32(txitem.Value.GetResult);
                    txitem.Value.CurrentValue = (E_SwitchOverMode)enumvalue;
                    txitem.Value.GetResult = ((E_SwitchOverMode)enumvalue).ToString();
                }
                else
                {
                    log.Write(string.Format("Unable to convert SNMP data to expected type for item: {0}", txitem.Value.Description));
                }   
            }
        } //end private void convertSnmpData(Dictionary<Oid, AsnType> result)

        // Note: it would be better if the Transmitter could be overridden using the
        // computer only. This can be re-written later

    }

    public class TxItem
    {
        public string Oid { get; set; }
        public string Description { get; set; }
        public dynamic CurrentValue { get; set; }
        public dynamic RequestValue { get; set; }
        public string GetResult { get; set; }
        public string DataType { get; set; }
        public int FaultCount { get; set; }
        public bool ReadOnly { get; set; }
    }

    public class TxResult
    {
        public string guiText { get; set; }
        public Tx.E_TxLinkStatus linkStat { get; set; }
        public int txNum { get; set; }
        public Dictionary<Oid, AsnType> result { get; set; }
    }
}
